package com.duck.user.websocket;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@CrossOrigin(origins = "*")
@ServerEndpoint(value = "/chat", configurator = JwtHandshakeConfig.class) // 使用基于 JWT 的握手配置器
public class ChatEndpoint {

    // 保存所有在线用户的 WebSocket Session（线程安全）
    private static final Map<String, Session> friendGroup = new ConcurrentHashMap<>();

    // 当前连接对应的用户 ID（从 JWT 中获取）
    private String userId;

    /**
     * 连接建立时调用：从握手时携带的 userProperties 中获取 userId，
     * 并将当前会话存入 friendGroup。
     *
     * @param session 当前 WebSocket 会话
     * @param config  握手时传递的配置信息，其中包含 JWT 解析后的 userId
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        // 从握手配置中获取 userId（由 JwtHandshakeConfig 设置）
        this.userId = (String) config.getUserProperties().get("userId");
        System.out.println("userId: " + this.userId);
        // 如果 userId 为空，则关闭连接
        if (this.userId == null || this.userId.isEmpty()) {
            log.error("建立连接时未能获取有效的用户ID");
            try {
                session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "无效的用户身份"));
            } catch (IOException e) {
                log.error("关闭 session 失败", e);
            }
            return;
        }

        // 保存该会话到在线会话组中
        friendGroup.put(this.userId, session);
        log.info("WebSocket 连接建立成功: {}", this.userId);

        // 广播系统消息：用户上线
        String content = "用户 " + this.userId + " 已经上线";
        Message message = Message.builder()
                .content(content)
                .isSystem(true)
                .build();
        broadcast(message);
    }

    /**
     * 连接关闭时调用：移除当前用户的会话，并广播下线消息。
     *
     * @param session 当前 WebSocket 会话
     */
    @OnClose
    public void onClose(Session session) {
        if (this.userId != null) {
            friendGroup.remove(this.userId);
            log.info("用户 {} 已下线", this.userId);

            // 广播系统消息：用户下线
            String content = "用户 " + this.userId + " 已经下线";
            Message message = Message.builder()
                    .content(content)
                    .isSystem(true)
                    .build();
            broadcast(message);
        }
    }

    /**
     * 接收到客户端消息时调用：解析消息并转发给指定接收者。
     *
     * @param session     当前 WebSocket 会话
     * @param messageJson 前端发送的 JSON 格式消息
     */
    @OnMessage
    public void onMessage(Session session, String messageJson) {
        log.info("接收到消息: {}", messageJson);

        // 解析消息：这里假设前端发送的消息 JSON 与 Message 对象字段一致，
        // 并且接收者的标识字段为 receiverUserId
        Message receivedMsg = JSON.parseObject(messageJson, Message.class);
        String senderId = this.userId;

        // 根据消息中的接收者 ID 获取目标会话
        Session receiverSession = friendGroup.get(receivedMsg.getToReceiver());
        if (receiverSession != null) {
            // 构造待发送的消息
            Message messageToSend = Message.builder()
                    .sender(senderId)
                    .toReceiver(receivedMsg.getToReceiver())
                    .content(receivedMsg.getContent())
                    .build();
            try {
                receiverSession.getBasicRemote().sendText(JSON.toJSONString(messageToSend));
            } catch (IOException e) {
                log.error("发送消息失败", e);
            }
        } else {
            log.warn("接收者 {} 不在线", receivedMsg.getToReceiver());
        }
    }

    /**
     * 当发生错误时调用。
     *
     * @param session 当前 WebSocket 会话
     * @param error   错误信息
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("WebSocket 错误: {}", error.getMessage(), error);
    }

    /**
     * 将系统公告或消息推送给所有在线用户。
     *
     * @param message 要推送的消息对象
     */
    private void broadcast(Message message) {
        friendGroup.forEach((userId, userSession) -> {
            try {
                userSession.getBasicRemote().sendText(JSON.toJSONString(message));
            } catch (IOException e) {
                log.error("消息推送失败", e);
            }
        });
    }
}
